Conditional generation of a string in a single XPath expression

By  Dimitre Novatchev
 
Language  XPATH
Category designpatterns, xml
Posted 28  Mar  2001
Updated 01  Apr  2001
 
Summary
This snippet shows how to generate in a single XPath expression a string, which depends on some other boolean XPath expression. It may be easily generalised to generate a string depending on a condition, which has more than 2 outcomes.
 
I love XPath for its power to select a node-set based on complex conditions.
 
At the same time such capabilities for specifying a string as opposed to a nodeset, seem non-existant. How often have you had to use a verbose multi-line xsl:choose construct just to specify that "in case1 use string1, in case2 use string2, ..., in caseN use stringN"?
 
And what if you have decided not to use an XSLT transformation but only the DOM methods -- then you don't have even xsl:choose to use...
 
In all such cases we feel the need of a technique, which would allow us to specify in a single XPath expression a string, which depends on condition(s).
 
Here's how to do it: If you remember another of my snippets for advanced sorting of dynamically constructed strings
(http://www.vbxml.com/snippetcentral/main.asp?view=viewsnippet&id=v20010227100924),
there I used the concat() function with two operands. The two operands were specified in such a way, that for any different pair of values they'd have, one would be a nonempty string, and the other would be the empty string.
 
We'll use the same approach now. We want an XPath expression, which returns a string when some given condition is true, and returns the empty string if this same condition is false.
 
We can think of "true" as "1" and of "false" as "0".
 
Zero comes handy -- the empty string has a length of zero.
 
But how to fit "1" to any string?
 
And which string - handling function can we use?
 
OK, substring() seems quite convenient. And here's the trick: we can use substring() with only two arguments.
 
substring(str,nOffset)
 
will return the (remainder of the) string str starting at offset nOffset.
 
In particular:
 
substring(str,1) returns the whole string
 
substring(str, nVeryLargeNumber) will return the empty string, if nVeryLargeNumber is guaranteed to be greater than any possible string length.
 
Do we know such a number? Hmm... Infinity?
 
So, the expression we might use would be:
 
concat( substring(str1,exp(Condition)), substring(str2,exp(not(Condition)) )
 
and we want exp(Condition) to be 1 if Condition is true, and exp(Condition) to be Infinity if Condition is false.
 
A last moment of thought and... Here it is:
 
We express exp(Condition) as:
 
1 div Condition
 
Because a boolean expression is first converted to a number (true -> 1, false -> 0), we get exactly:
 
exp(true) = 1 exp(false) = Infinity.
 
To summarise: The XPath expression returning Str1 if a condition Cond is true and returning Str2 if this same condition Cond is false -- this is:
 
concat( substring(Str1,1 div Cond), substring(Str2,1 div not(Cond)) )
 
The technique I described can easily be generalised to specify an XPath expression, which generates one of given N strings depending on the value of another XPath expression, which has exactly N possible different values.
 
I leave this to you as an exercise.
 
To illustrate what we have done, here's a small example. I want to have a template, which generates the text: "My girlfriend" when it is passed a parameter "Mary" and to generate the text "Some other girl" if the value of the parameter is not "Mary".
 
Of course, no xsl:if -s or xsl:when -s are allowed.
 
Here's the code, and when applied to any xml source document, it generates:
 
Mary:
 My girlfriend
Ann:
 Some other girl
 
 
XML Source is N/A
 
 
XSL Stylesheet
XSL Stylesheet
 
 
XML/HTML Result:
 
 
Text Result: